package com.yafeng.shiro;

import com.yafeng.logic.mapper.UserOperatorMapper;
import com.yafeng.logic.service.UserOperatorService;
import com.yafeng.utils.JwtUtils;
import com.yafeng.utils.RedisUtils;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;


@Slf4j
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserOperatorService userOperatorService;

    @Autowired
    private RedisUtils redisUtils;

    @Value("${token.app_subject}")
    private String app_subject;
    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {

        //return token instanceof UsernamePasswordToken;
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        System.out.println("授权");
        //String username = JwtUtils.getUsername(principals.toString());
        // UserBean user = userService.queryUserById(username);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // simpleAuthorizationInfo.addRole(user.getRole());
        // Set<String> permission = new HashSet<>(Arrays.asList(user.getPermission().split(",")));
        // simpleAuthorizationInfo.addStringPermissions(permission);
        return simpleAuthorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     * 我们可以看到doGetAuthenticationInfo里面只判断了用户存不存在，其实也没做密码比对，
     * 只是把数据库的数据封装一下就返回了。
     * 真正的比对逻辑在Matcher里实现的，这个shiro已经替我们实现了。如果matcher返回false，shiro会抛出异常，
     * 这样controller那边就会知道验证失败了。
     */
    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {

        String token = (String) auth.getPrincipal();

        //jwt验证
        String verifyToken=token;
        if(!JwtUtils.verify(token)){

            //验证失败！
            if(redisUtils.hasKey(token)){  //如果redis中还有的话就是超过2分钟的jwt时间  重新获取   token无痛刷新

                log.info("token过期");
                String access_token =  redisUtils.strGet(token);

                //解析access_token

                Claims claims = JwtUtils.getClaims(access_token);
                String subject = (String) claims.get("username");

                //从新生成newtoken
                String newToken = userOperatorService.generateJwtToken((String) claims.get("username"), RedisUtils.expirationSeconds);

                //删除redis过期的key
                redisUtils.delete(token);

                //生成新的redis
                redisUtils.set(newToken,access_token, redisUtils.validTime); //重新7天

                //设置cookie
                userOperatorService.generateTokenCookie(null,newToken);


            }else{

                log.info("token伪造或者超过7天重新获取"); //需要前端从新授权
                verifyToken="test";
            }



        }

        //转交给shiro处理
        SimpleAuthenticationInfo authenticationInfo= new SimpleAuthenticationInfo(token, verifyToken, "myRealm");
        return authenticationInfo;

    }

}

















